package com.liang.lingdong.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liang.lingdong.common.ErrorCode;
import com.liang.lingdong.constant.UserConstant;
import com.liang.lingdong.enums.UserRoleEnum;
import com.liang.lingdong.exception.BusinessException;
import com.liang.lingdong.model.exam.Exam;
import com.liang.lingdong.model.sign.Sign;
import com.liang.lingdong.model.topic.Topic;
import com.liang.lingdong.model.topic.dto.CreateTopicDto;
import com.liang.lingdong.model.topic.dto.QueryTopicListDto;
import com.liang.lingdong.model.topic.dto.UpdateTopicDto;
import com.liang.lingdong.model.topic.vo.TopicVo;
import com.liang.lingdong.service.ExamService;
import com.liang.lingdong.service.SignService;
import com.liang.lingdong.service.TopicService;
import com.liang.lingdong.mapper.TopicMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
* @author liang
* @description 针对表【topic(题目表)】的数据库操作Service实现
* @createDate 2024-11-29 21:34:17
*/
@Service
public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic>
    implements TopicService{

    @Resource
    private ExamService examService;

    @Resource
    private SignService signService;

    /**
     * 创建题目
     * @param createTopicDto 题目参数
     * @return 结果
     */
    @Override
    public Long createTopic(CreateTopicDto createTopicDto) {
        Long examId = createTopicDto.getExamId();
        // 创建题目
        Topic topic = BeanUtil.toBean(createTopicDto, Topic.class);
        // 如果比赛不存在
        if (examId != null) {
            // 如果设置了比赛id，验证比赛是否存在
            Exam exam = examService.lambdaQuery()
                    .eq(Exam::getBusinessId, examId)
                    .one();
            if (exam == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"比赛不存在");
            }
            // 如果比赛已经在进行中则不允许创建 根据时间判断
            Date startTime = exam.getStartTime();
            if (new Date().after(startTime) || new Date().after(exam.getEndTime())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"比赛进行中或已结束，不允许创建题目");
            }
        }
        // 设置为隐私题目
        topic.setStatus(2);
        boolean save = this.save(topic);
        if (!save) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"创建题目失败");
        }
        return topic.getBusinessId();
    }

    /**
     * 更新题目
     * @param updateTopicDto 题目参数
     * @return 结果
     */
    @Override
    public boolean updateExam(UpdateTopicDto updateTopicDto) {
        Long businessId = updateTopicDto.getBusinessId();
        // 校验题目是否存在
        Topic topic = this.getById(businessId);
        if (topic == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"题目不存在");
        }
        if (updateTopicDto.getExamId() != null) {
            // 如果设置了比赛id，验证比赛是否存在
            Exam exam = examService.lambdaQuery()
                    .eq(Exam::getBusinessId, updateTopicDto.getExamId())
                    .one();
            // 如果比赛不存在
            if (exam == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"比赛不存在");
            }
            // 如果比赛已经在进行中则不允许修改比赛id 根据时间判断
            Date startTime = exam.getStartTime();
            if (new Date().after(startTime) || new Date().after(exam.getEndTime())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"比赛进行中或已结束，不允许换绑题目");
            }
            // 设置比赛id
            topic.setExamId(updateTopicDto.getExamId());
        }else {
            topic.setExamId(null);
        }

        Topic updateTopic = BeanUtil.toBean(updateTopicDto, Topic.class);
        return this.updateById(updateTopic);

    }

    /**
     * 删除题目
     * @param aLong 题目id
     * @return 结果
     */
    @Override
    public boolean deleteTopicById(Long aLong) {
        Topic topic = this.getById(aLong);
        // 判断题目是否失效
        if (topic == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"题目不存在");
        }
        // 判断题目是否绑定考试
        if (topic.getExamId() != null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"题目已绑定考试，无法删除");
        }
        return this.removeById(aLong);
    }

    /**
     * 查看题目列表
     * @param examId 考试id
     * @return 结果
     */
    @Override
    public List<TopicVo> getTopicListByExamId(Long examId) {
        // 判断是不是管理员
        boolean isAdmin = StpUtil.hasRole(String.valueOf(UserRoleEnum.ADMIN.getCode()));
        if (isAdmin) {
            List<Topic> topicList = lambdaQuery()
                    .eq(Topic::getExamId, examId)
                    .list();
            // 转换为vo
            return BeanUtil.copyToList(topicList, TopicVo.class);
        }else {
            // 如果不是管理员则需要校验比赛是否在进行中
            Exam exam = examService.lambdaQuery()
                    .eq(Exam::getBusinessId, examId)
                    .one();
            if (exam == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"考试不存在");
            }
            if (new Date().after(exam.getStartTime()) && new Date().before(exam.getEndTime())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"考试未开始或已结束");
            }
            // 校验当前登录人是否报名该比赛
            Long userId = StpUtil.getLoginIdAsLong();
            Sign sign = signService.lambdaQuery()
                    .eq(Sign::getExamId, examId)
                    .eq(Sign::getUserId, userId)
                    .one();
            if (sign == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"请先报名该考试");
            }
            // 查询题目列表
            List<Topic> topicList = lambdaQuery()
                    .eq(Topic::getExamId, examId)
                    .list();
            // 转换为vo
            return BeanUtil.copyToList(topicList, TopicVo.class);
        }
    }

    /**
     * 解绑题目
     * @param topicId 题目id
     * @return 结果
     */
    @Override
    public boolean unbindTopic(Long topicId) {
        Topic topic = this.getById(topicId);
        if (topic == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"题目不存在");
        }
        if (topic.getExamId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"题目未绑定考试");
        }
        // 判断考试进行中无法解绑
        Exam exam = examService.lambdaQuery()
                .eq(Exam::getBusinessId, topic.getExamId())
                .one();
        if (exam == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"考试不存在");
        }
        if (new Date().after(exam.getStartTime()) && new Date().before(exam.getEndTime())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"考试进行中或已结束，无法解绑题目");
        }
        // 解绑
        return lambdaUpdate()
                .eq(Topic::getBusinessId, topicId)
                .set(Topic::getExamId, null)
                .update();
    }

    /**
     * 分页查询题目列表
     * @param queryTopicListDto 查询参数
     * @return 结果
     */
    @Override
    public Page<TopicVo> queryTopicListByPage(QueryTopicListDto queryTopicListDto) {
        String topicName = queryTopicListDto.getTopicName();
        Long examId = queryTopicListDto.getExamId();
        Integer topicType = queryTopicListDto.getTopicType();
        long current = queryTopicListDto.getCurrent();
        long pageSize = queryTopicListDto.getPageSize();
        // 构造查询条件
        Page<Topic> topicPage = lambdaQuery()
                .like(StringUtils.isNotBlank(topicName), Topic::getTopicName, topicName)
                .eq(examId != null, Topic::getExamId, examId)
                .eq(topicType != null, Topic::getTopicType, topicType)
                .page(new Page<>(current, pageSize));
        // 构造返回结果
        List<Topic> records = topicPage.getRecords();
        List<TopicVo> topicVoList = BeanUtil.copyToList(records, TopicVo.class);
        Page<TopicVo> topicVoPage = new Page<>();
        BeanUtil.copyProperties(topicPage, topicVoPage);
        topicVoPage.setRecords(topicVoList);
        return topicVoPage;
    }
}




